package replay

import (
	"container/list"
	"database/sql"
	"database/sql/driver"
	"fmt"
	"github.com/fcbox/dbreplay/dbreplay-server/util"
	_ "github.com/go-sql-driver/mysql"
	"io"
	"strconv"
	"strings"
	"sync"
	"time"
)
var ReadM map[string] *sql.DB = make(map[string] *sql.DB)
type Replay struct {
	lsts [100]*list.List
	locks [100] *sync.Mutex
	lock sync.Mutex
	Tip string
	Tport int
	Tuser string
	Tpassword string
	Tdb string
	FileObj io.Writer
	ErrFileObj io.Writer
}

func NewReplay() *Replay  {
	replay := new(Replay)
	return replay
}
func (replay *Replay)Dispatch(session,sql string)  {
	comm := new(util.Command)
	comm.Session = session
	comm.CommandStr = util.ReplacePay(sql)

	hashCode := util.StringHashcode(session)
	i := hashCode % 100
	replay.locks[i].Lock()
	replay.lsts[i].PushBack(comm)
	replay.locks[i].Unlock()
}

func(replay *Replay) Init()  {
	//mapp := new(map[string] *sql.DB)
	//replay.ReadM = mapp
	for i:=0; i<100; i++{
		replay.lsts[i]=list.New()
		replay.locks[i]= new(sync.Mutex)
	}
	for i:=0; i<100; i++{
		go replay.doCommand(i)
	}
}
func (replay *Replay)doCommand(i int)  {

	var lst *list.List
	lst = replay.lsts[i]
	for{
		replay.locks[i].Lock()
		comm :=lst.Front()
		replay.locks[i].Unlock()
		if comm == nil{
			time.Sleep(100*time.Millisecond)
			continue
		}
		if comm.Value == nil{
			continue
		}
		commond :=comm.Value.(*util.Command)

		if strings.HasPrefix(commond.CommandStr,"LOGIN"){
			replay.locks[i].Lock()
			lst.Remove(comm)
			replay.locks[i].Unlock()
			continue
		}
		replay.lock.Lock()
		DB,_ := ReadM[commond.Session]
		replay.lock.Unlock()
		if DB == nil {
			dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",replay.Tuser,replay.Tpassword,"tcp",replay.Tip,replay.Tport,replay.Tdb)
			//util.WriteString(replay.ErrFileObj,dsn+"\n")
			DBN,err := sql.Open("mysql",dsn)
			if err != nil{
				util.WriteString(replay.ErrFileObj,"Open mysql failed,err:"+err.Error()+"\n")
				continue
			}
			DBN.SetConnMaxLifetime(1000*time.Minute)  //最大连接周期，超过时间的连接就close
			DBN.SetMaxOpenConns(1) //设置最大连接数
			DBN.SetMaxIdleConns(1) //设置闲置连接数
			replay.lock.Lock()
			ReadM[commond.Session] = DBN
			DB = DBN
			replay.lock.Unlock()
		}
		if strings.HasPrefix(commond.CommandStr,"CLOSE"){
			DB.Close()
			replay.locks[i].Lock()
			lst.Remove(comm)
			replay.locks[i].Unlock()
			continue
		}
		if time.Now().Minute() == 10 || time.Now().Minute() == 15 ||time.Now().Minute() == 45{
			replay.locks[i].Lock()
			lst.Remove(comm)
			replay.locks[i].Unlock()
			continue
		}
		/*
		if (!strings.HasPrefix(commond.CommandStr,"select")) && (!strings.HasPrefix(commond.CommandStr,"SELECT")){
			replay.locks[i].Lock()
			lst.Remove(comm)
			replay.locks[i].Unlock()
			continue
		}*/
		be := time.Now().Unix()
		_,err :=DB.Exec(commond.CommandStr)
		en := time.Now().Unix()-be
		if en > 3{
			util.WriteString(replay.ErrFileObj,"execute time is:"+strconv.FormatInt(en,10)+":"+commond.Session+":"+commond.CommandStr+time.Now().String()+"\n")
			//fmt.Println("execute time is:",en,time.Now(),commond.Session,commond.CommandStr,err)
		}
		if err != nil{
			if  err == driver.ErrBadConn || strings.Contains(err.Error(),"close"){
				DB.Close()
				DB = nil
				dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",replay.Tuser,replay.Tpassword,"tcp",replay.Tip,replay.Tport,replay.Tdb)
				DBN,err := sql.Open("mysql",dsn)
				if err != nil{
					util.WriteString(replay.ErrFileObj,"Open mysql failed,err:"+err.Error()+"\n")
					continue
				}
				DBN.SetConnMaxLifetime(1000*time.Minute)  //最大连接周期，超过时间的连接就close
				DBN.SetMaxOpenConns(1) //设置最大连接数
				DBN.SetMaxIdleConns(1) //设置闲置连接数
				replay.lock.Lock()
				ReadM[commond.Session] = DBN
				DB = DBN
				replay.lock.Unlock()
				continue
			}
			util.WriteString(replay.ErrFileObj,"catch error :" + strconv.FormatInt(en,10)+":"+commond.Session+":"+commond.CommandStr+":"+err.Error()+commond.CommandStr+time.Now().String()+"\n")

		}
		replay.locks[i].Lock()
		lst.Remove(comm)
		replay.locks[i].Unlock()
	}
}